package net.ufrog.common.utils;

import net.ufrog.common.Logger;
import net.ufrog.common.exception.ServiceException;
import org.apache.commons.codec.binary.Base64;

import javax.imageio.IIOImage;
import javax.imageio.ImageIO;
import javax.imageio.ImageWriteParam;
import javax.imageio.ImageWriter;
import javax.imageio.stream.FileImageOutputStream;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;

/**
 * 图片工具
 * 
 * @author ultrafrog
 * @version 1.0, 2014-02-13
 * @since 1.0
 */
public class Images {

    private static final String BASE64 = "data:%s;base64,%s";

    /** 构造函数 */
    private Images() {}

    /**
     * 转换图片
     *
     * @param mimeType 扩展类型
     * @param bytes 字节数组
     * @return 加密后字符串
     */
    public static String toBase64(String mimeType, byte[] bytes) {
        return String.format(BASE64, mimeType, new String(Base64.encodeBase64(bytes)));
    }

    /**
     * 转换图片
     *
     * @param image 图片文件
     * @return 加密后字符串
     */
    public static String toBase64(File image) {
        return toBase64(Files.getMimeType(image), Files.toBytes(image));
    }

    /**
     * 修改图片尺寸
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param to 目标文件
     * @param width 宽度
     * @param height 高度
     * @param keepRatio 是否保持比例
     */
    public static void resize(InputStream input, String mimeType, File to, Integer width, Integer height, Boolean keepRatio) {
        try (FileImageOutputStream fios = new FileImageOutputStream(to)) {
            BufferedImage source = ImageIO.read(input);
            Integer fWidth = source.getWidth();
            Integer fHeight = source.getHeight();
            Integer mWidth = width;
            Integer mHeight = height;
            Double ratio = fWidth.doubleValue() / fHeight.doubleValue();

            if (width < 0 && height < 0) { width = fWidth; height = fHeight; }
            if (width < 0 && height > 0) { width = Double.valueOf(height * ratio).intValue(); }
            if (width > 0 && height < 0) { height = Double.valueOf(width / ratio).intValue(); }
            if (keepRatio) {
                height = Double.valueOf(width / ratio).intValue();
                if (height > mHeight) { height = mHeight; width = Double.valueOf(height * ratio).intValue(); }
                if (width > mWidth) { width = mWidth; height = Double.valueOf(width / ratio).intValue(); }
            }

            BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
            Image resized = source.getScaledInstance(width, height, Image.SCALE_SMOOTH);
            Graphics graphics = dest.getGraphics();
            graphics.setColor(Color.WHITE);
            graphics.fillRect(0, 0, width, height);
            graphics.drawImage(resized, 0, 0, null);

            ImageWriter writer = ImageIO.getImageWritersByMIMEType(mimeType).next();
            ImageWriteParam params = writer.getDefaultWriteParam();
            writer.setOutput(fios);

            IIOImage img = new IIOImage(dest, null, null);
            writer.write(null, img, params);
            fios.flush();
        } catch (IOException e) {
            throw new ServiceException("cannot resize image.", e);
        }
    }

    /**
     * 修改图片尺寸
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param to 目标文件
     * @param width 宽度
     * @param height 高度
     */
    public static void resize(InputStream input, String mimeType, File to, Integer width, Integer height) {
        resize(input, mimeType, to, width, height, Boolean.FALSE);
    }

    /**
     * 修改图片尺寸
     *
     * @param image 图片文件
     * @param to 目标文件
     * @param width 宽度
     * @param height 高度
     * @param keepRatio 是否保持比例
     */
    public static void resize(File image, File to, Integer width, Integer height, Boolean keepRatio) {
        try (FileInputStream fis = new FileInputStream(image)) {
            resize(fis, Files.getMimeType(image), to, width, height, keepRatio);
        } catch (IOException e) {
            throw new ServiceException("cannot resize image.", e);
        }
    }

    /**
     * 修改图片尺寸
     *
     * @param image 图片文件
     * @param to 目标文件
     * @param width 宽度
     * @param height 高度
     */
    public static void resize(File image, File to, Integer width, Integer height) {
        resize(image, to, width, height, Boolean.FALSE);
    }

    /**
     * 修改图片尺寸
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param width 宽度
     * @param height 高度
     * @param keepRatio 是否保持比例
     * @return 目标文件字节数组
     */
    public static byte[] resize(InputStream input, String mimeType, Integer width, Integer height, Boolean keepRatio) {
        File to = Files.generateTmpFile();
        resize(input, mimeType, to, width, height, keepRatio);
        byte[] bytes = Files.toBytes(to);
        if (!to.delete()) Logger.warn("cannot delete tmp file: %s", to.getAbsolutePath());
        return bytes;
    }

    /**
     * 修改图片尺寸
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param width 宽度
     * @param height 高度
     * @return 目标文件字节数组
     */
    public static byte[] resize(InputStream input, String mimeType, Integer width, Integer height) {
        return resize(input, mimeType, width, height, Boolean.FALSE);
    }

    /**
     * 裁剪图片
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param to 目标文件
     * @param x 横坐标
     * @param y 纵坐标
     * @param width 宽度
     * @param height 高度
     */
    public static void crop(InputStream input, String mimeType, File to, Integer x, Integer y, Integer width, Integer height) {
        try (FileImageOutputStream fios = new FileImageOutputStream(to)) {
            BufferedImage source = ImageIO.read(input);
            BufferedImage dest = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);

            Image cropped = source.getSubimage(x, y, width, height);
            Graphics graphics = dest.getGraphics();
            graphics.setColor(Color.WHITE);
            graphics.fillRect(0, 0, width, height);
            graphics.drawImage(cropped, 0, 0, null);

            ImageWriter writer = ImageIO.getImageWritersByMIMEType(mimeType).next();
            ImageWriteParam params = writer.getDefaultWriteParam();
            writer.setOutput(fios);

            IIOImage img = new IIOImage(dest, null, null);
            writer.write(null, img, params);
            fios.flush();
        } catch (IOException e) {
            throw new ServiceException("cannot crop image.", e);
        }
    }

    /**
     * 裁剪图片
     *
     * @param image 图片文件
     * @param to 目标文件
     * @param x 横坐标
     * @param y 纵坐标
     * @param width 宽度
     * @param height 高度
     */
    public static void crop(File image, File to, Integer x, Integer y, Integer width, Integer height) {
        try (FileInputStream fis = new FileInputStream(image)) {
            crop(fis, Files.getMimeType(image), to, x, y, width, height);
        } catch (IOException e) {
            throw new ServiceException("cannot crop image.", e);
        }
    }

    /**
     * 裁剪图片
     *
     * @param input 输入流
     * @param mimeType 扩展类型
     * @param x 横坐标
     * @param y 纵坐标
     * @param width 宽度
     * @param height 高度
     */
    public static byte[] crop(InputStream input, String mimeType, Integer x, Integer y, Integer width, Integer height) {
        File to = Files.generateTmpFile();
        crop(input, mimeType, to, x, y, width, height);
        byte[] bytes = Files.toBytes(to);
        if (!to.delete()) Logger.warn("cannot delete tmp file: %s", to.getAbsolutePath());
        return bytes;
    }
}
